//===--- ClassLayout.h - Class instance layout ------------------*- C++ -*-===//
//
// This source file is part of the Swift.org open source project
//
// Copyright (c) 2014 - 2017 Apple Inc. and the Swift project authors
// Licensed under Apache License v2.0 with Runtime Library Exception
//
// See https://swift.org/LICENSE.txt for license information
// See https://swift.org/CONTRIBUTORS.txt for the list of Swift project authors
//
//===----------------------------------------------------------------------===//
//
// This file defines some routines that are useful for calculating class
// instance layout.
//
//===----------------------------------------------------------------------===//

#ifndef POLARPHP_IRGEN_INTERNAL_CLASSLAYOUT_H
#define POLARPHP_IRGEN_INTERNAL_CLASSLAYOUT_H

#include "llvm/ADT/ArrayRef.h"
#include "polarphp/irgen/internal/IRGen.h"
#include "polarphp/irgen/internal/StructLayout.h"

namespace polar::irgen {

/// Different policies for accessing a physical field.
enum class FieldAccess : uint8_t {
   /// Instance variable offsets are constant.
      ConstantDirect,

   /// Instance variable offsets must be loaded from "direct offset"
   /// global variables.
      NonConstantDirect,

   /// Instance variable offsets are kept in fields in metadata, but
   /// the offsets of those fields within the metadata are constant.
      ConstantIndirect
};

/// A set of flags describing properties of a class's metadata layout.
/// The presence or absence of these flags determines how much static
/// knowledge the compiler has of the layout of this class and its
/// metadata, which in turn will determine the strategy used to emit
/// and initialize class metadata.
enum class ClassMetadataFlags {
   /// Does the class or any of its superclasses have stored properties that
   /// where dropped due to the Swift language version availability of
   /// their types?
      ClassHasMissingMembers = (1 << 0),

   /// Does the class or any of its fragile superclasses have stored
   /// properties of unknown size, which do *not* depend on generic
   /// parameters?
   ///
   /// This is different from the class itself being resilient or
   /// having resilient ancestry, because we still have a fixed layout
   /// for the class metadata in this case.
   ///
   /// In fact, for a class with resilient ancestry, this can still be
   /// false if all of the fields known to us are fixed size.
      ClassHasResilientMembers = (1 << 1),

   /// Is this class or any of its superclasses generic?
      ClassHasGenericAncestry = (1 << 2),

   /// Is this class itself generic via the Swift generic system, ie. not a
   /// lightweight Objective-C generic class?
      ClassIsGeneric = (1 << 3),

   /// Does the class layout depend on the size or alignment of its
   /// generic parameters?
   ///
   /// This can be the case if the class has generic resilient ancestry
   /// that depends on the class's generic parameters, of it it has
   /// fields of generic type that are not fixed size.
      ClassHasGenericLayout = (1 << 4),

   /// Is this class or any of its superclasses resilient from the viewpoint
   /// of the current module? This means that their metadata can change size,
   /// hence field offsets, generic arguments and virtual methods must be
   /// accessed relative to a metadata base global variable.
   ///
   /// Note that a @_fixed_layout class in a resilient module still has
   /// resilient metadata, so any subclasses will have this flag set;
   /// to check for resilient stored property layout, check for
   /// ClassHasResilientMembers.
      ClassHasResilientAncestry = (1 << 5),

   /// Are any of this class's superclasses defined in Objective-C?
   /// This means that field offsets must be loaded from field offset globals
   /// or the field offset vector in the metadata, and the Objective-C runtime
   /// will slide offsets based on the actual superclass size, which is not
   /// known at compile time.
      ClassHasObjCAncestry = (1 << 6)
};

using ClassMetadataOptions = OptionSet<ClassMetadataFlags>;

class ClassLayout {
   /// The statically-known minimum bound on the alignment.
   Alignment MinimumAlign;

   /// The statically-known minimum bound on the size.
   Size MinimumSize;

   /// Whether this layout is fixed in size. If so, the size and
   /// alignment are exact.
   bool IsFixedLayout;

   ClassMetadataOptions Options;

   /// The LLVM type for instances of this class.
   llvm::Type *Ty;

   /// Lazily-initialized array of all fragile stored properties directly defined
   /// in the class itself.
   ArrayRef<VarDecl *> AllStoredProperties;

   /// Lazily-initialized array of all field access methods.
   ArrayRef<FieldAccess> AllFieldAccesses;

   /// Fixed offsets of fields, if known (does not take Objective-C sliding into
   /// account).
   ArrayRef<ElementLayout> AllElements;

public:
   ClassLayout(const StructLayoutBuilder &builder,
               ClassMetadataOptions options,
               llvm::Type *classTy,
               ArrayRef<VarDecl *> allStoredProps,
               ArrayRef<FieldAccess> allFieldAccesses,
               ArrayRef<ElementLayout> allElements);

   Size getInstanceStart() const;

   llvm::Type *getType() const { return Ty; }
   Size getSize() const { return MinimumSize; }
   Alignment getAlignment() const { return MinimumAlign; }
   Size getAlignMask() const { return getAlignment().asSize() - Size(1); }

   bool isFixedLayout() const { return IsFixedLayout; }

   /// Returns true if the stored property layout of instances of this class
   /// is known at compile time.
   ///
   /// Note that ClassHasResilientAncestry or ClassHasGenericAncestry might
   /// still be true; the former means the class has resilient metadata, so
   /// it might still be @_fixed_layout; the latter means we have a generic
   /// superclass, but it doesn't mean the layout actually depends on any
   /// generic parameters.
   bool isFixedSize() const {
      return !(Options.contains(ClassMetadataFlags::ClassHasMissingMembers) ||
               Options.contains(ClassMetadataFlags::ClassHasResilientMembers) ||
               Options.contains(ClassMetadataFlags::ClassHasGenericLayout) ||
               Options.contains(ClassMetadataFlags::ClassHasObjCAncestry));
   }

   /// Returns true if the runtime may attempt to assign non-zero offsets to
   /// empty fields for this class.  The ObjC runtime will do this if it
   /// decides it needs to slide ivars.  This is the one exception to the
   /// general rule that the runtime will not try to assign a different offset
   /// than was computed statically for a field with a fixed offset.
   bool mayRuntimeAssignNonZeroOffsetsToEmptyFields() const {
      return Options.contains(ClassMetadataFlags::ClassHasObjCAncestry);
   }

   /// Returns true iff everything about the class metadata layout is statically
   /// known except field offsets and the instance size and alignment.
   ///
   /// Will assert if the class metadata is "more" dynamic; you must check
   /// doesMetadataRequireRelocation() and doesMetadataRequireInitialization()
   /// first.
   bool doesMetadataRequireUpdate() const {
      assert(!doesMetadataRequireInitialization());
      return (Options.contains(ClassMetadataFlags::ClassHasResilientMembers) ||
              Options.contains(ClassMetadataFlags::ClassHasMissingMembers));
   }

   /// Returns true iff everything about the class metadata layout is statically
   /// known except the superclass field must be instantiated at runtime because
   /// it is a generic class type.
   ///
   /// Will assert if the class metadata is "more" dynamic; you must check
   /// doesMetadataRequireRelocation() first.
   bool doesMetadataRequireInitialization() const {
      assert(!doesMetadataRequireRelocation());
      return Options.contains(ClassMetadataFlags::ClassHasGenericAncestry);
   }

   /// Returns true if the class metadata must be built at runtime because its
   /// size is not known at compile time. This is the most general case.
   bool doesMetadataRequireRelocation() const {
      return (Options.contains(ClassMetadataFlags::ClassHasResilientAncestry) ||
              Options.contains(ClassMetadataFlags::ClassIsGeneric));
   }

   std::pair<FieldAccess, ElementLayout>
   getFieldAccessAndElement(VarDecl *field) const {
      // FIXME: This is algorithmically terrible.
      auto found = std::find(AllStoredProperties.begin(),
                             AllStoredProperties.end(), field);
      assert(found != AllStoredProperties.end() && "didn't find field in type?!");
      unsigned index = found - AllStoredProperties.begin();

      return std::make_pair(AllFieldAccesses[index], AllElements[index]);
   }
};

} // end namespace polar::irgen

#endif // POLARPHP_IRGEN_INTERNAL_CLASSLAYOUT_H
